과제 1. Sound

  • 파일명은 hw1_sound.py로 제출

참고 site

소리의 기초

  • 소리는 지극히 주관적으로 청력에 따라 loudness라고 부르는 소리의 세기를 다르게 느낀다.
  • 소리의 세기를 결정하는 주요한 요소는 음입과 주파수이다.
  • 다음 그래프는 평범한 '사람'이 비슷한 세기로 느끼는 음압과 주파수의 조합이다.

Equal-loudness contours from ISO 226:2003 shown with original ISO standard.

음압 sound pressure level, volume

  • 소리는 공기의 압력 변화로 전달된다. 마이크는 주위 공기압을 측정하여 숫자로 변환시키는 역할을 한다.
  • 이 값들을 sample이라고 부르고 압력의 증가나 감소를 측정한다.

  • 소리 sample은 음이나 양의 정수로 이루어져 있다. 이는 기준압력에 대한 상대적인 압력을 측정한다.

  • 측정 압력의 상대적인 크기와 사람이 인지하는 소리의 상대적인 크기는 아주 다르다.
  • 보통 사람들은 100g에 1g을 더했을떄 느끼는 차이와 10kg에 100g을 더했을 떄 그 차이를 비슷하게 느낀다.
  • 얍력의 경우 그 차이를 decibel로 표시하여 절대적인 값의 차이가 아니라 상대적인 차이를 측정한다.
  • Decibel은 압력의 log 변환으로 전력은 10, 전압, 전류, 또는 음압을 측정할 때는 20을 곱해 사용한다.
$$ \text{dB} = 20 \log_{10} \frac{P}{P_{\text{ref}}} $$
  • 10dB에서 1dB의 차이는 10P, 30dB에서 1db의 차이는 258.9P가 된다.
    이러한 이유로 음압을 2배로 늘려도 사람의 귀는 그 차이를 거의 느끼지 못한다.

주파수 frequncies

  • 일반적인 가청 주파수는 20Hz~20kHz 사이이다.
  • 같은 음압이라면 1kHz 내외의 중음이 가장 크게 들리고 고음과 저음 순서로 강도가 떨어진다.
  • Python에서 주파수를 분석하려면 numpy나 scipy의 Fast Fourier Transform, ftt module을 사용할 수 있다.

Sampling rate와 bits

  • 1초에 기록하는 압력의 변화의 빈도를 sampling rate이라고 부른다.
  • 보통 CD의 경우 44,100번, 일상적인 대화를 선명히 들을 수 있는 sampling rate은 11,025(CD의 1/4)정도이다.
  • Bit는 sample의 해상도로 16 bits는 $2^{16}$, 즉 65,536단계로 압력을 구분하여 기록한다.
  • 고음질의 기준인 192k sampling rate에 24 bits은 1초를 192,000번으로 구분하고 압력을 16,777,216 단계로 구분한 것이다.

이용할 package

import numpy as np
import matplotlib.pyplot as plt
from scipy.io.wavfile import read, write
from IPython.display import Audio
  • scipy.io.wavfile은 wav 파일의 입출력에 사용한다.
  • scipy.io.wavfile.read(sound.wav) 으로 wav 파일을 읽으면 (sampling rate, samples)인 tuple로 변환한다.
  • samples는 왼쪽/오른쪽 channel을 기록한 shape이 (n,2)인 array이다.
(44100,
 array([[-2419712, 15728128],
        [ 2419712, 26011648],
        [14518272, 44764416],
        ...,
        [     256,   -13568],
        [     512,    -7424],
        [     512,    -2816]]))
  • IPython.display.Audio는 jupyter notebook에서 array나 음원파일, URL을 재생할 때 사용한다.
  • IPython.display.Audio에 array 형태의 2-chennel 자료를 입력할 때에는 Shape=(NCHAN, NSAMPLES)로 변환하여 사용한다.

과제

  • 이 과제의 예제에선 같은 폴더에 저장되어 있는 woosh.wav 파일을 내려받아 사용하였다.
  • text file이 아니므로 파일을 열때는 binary option을 추가한다.
  • 과제의 code를 작성할 때는 아래 code block을 실행시켜 답을 확인한다.
import requests

url = "https://raw.githubusercontent.com/k5yi/econ2005/master/datasets/kid_laugh.wav"
laugh = requests.get(url).content

with open('kid_laugh.wav', 'wb') as f:
    f.write(laugh)

fs, data = read('kid_laugh.wav')
sample frequency:  44100

예제: sample rate

위 파일의 sample rate은 44,100이므로 이를 주어진 비율로 줄이는 함수를 만들어 보자.

down_sampling(fs, sound, by=0.25)

함수 code만 hw1.py에 포함시켜 과제로 제출한다.

def down_sampling(sample, fs=44100, by=0.25):
    step = int(1/by)
    return int(fs*by), data[::step, :]

파일 크기를 90% 줄이면 음질에 상당한 영향을 미친다.

rate, downsample = down_sampling(sample, by=0.1)
Audio(downsample.T, rate=rate)

역재생 reversing a sound

  • 역재생은 sample의 순서를 뒤집어 놓은 것이다.
  • 음원을 거꾸로 재생하는 함수를 만든다.
fs, rev_sound = reverse_play(data)
Audio(rev_sound.T, rate=fs)

volume

  • 음압을 조정한다. scale만 변화시키면 된다.
  • 오디오기기의 볼륨과 같이 log scale로 소리를 크고 작게하는 함수를 만든다.
$$\text{level} = 20 \log_{10} \frac{\text{volume}}{{\text{default volume}}}$$
  • level 0은 원래 음량에 해당하며 level 1은 원래 음량의 1.12배, level = 6은 2배, level 20은 10배에 해당한다.
fs, amplified = amplify(sample level=6)
Audio(amplified.T, rate=fs)

Echo

  • sample rate 기준 10~20% 정도 delay를 주고 이를 몇 번 반복해서 삽입한다.
  • delay의 단위는 초, 반복할 때는 음량을 이전 효과의 20~25\% 수준으로 감소시킨다.
echo = echo_effect(sample fs=44100, delay=0.5, repeat=2, effect=.2)
Audio(echo.T, rate=fs)
  • 소리는 매 1초마다 2번 반복되며 따라서 재생시간은 2초 늘어난다.

mixing sounds

  • mixing은 여러 음원을 동시에 재생한다는 의미이다. 즉 각 위치의 sample value를 더하여 구한다.
  • 음압을 더하는 것이므로 너무 많은 음원을 mix하면 그냥 시끄러운 소리가 되어 버린다. (clipping)
  • 청각의 특징으로 인해 몇 가지 소리를 더하는 것으로는 큰 차이를 느끼지는 못한다.
  • $w = (w_1, w_2, \cdots, w_k), \sum w_i = 1$을 가중값으로 각 음원을 더하는 함수를 만든다.
fs, mix = mixing([sample, woosh], weight=(.4, .6))
Audio(mix.T, rate=fs)
  • 길이가 다른 음원들을 더해 얻은 파일의 길이는 더한 파일 중 가장 긴 길이의 음원과 같아야 한다.
  • 아래 예는 다음 음원을 이용하였다.
import requests

url = "https://raw.githubusercontent.com/k5yi/econ2005/master/datasets/woosh.wav"
woosh = requests.get(url).content

with open('woosh.wav', 'wb') as f:
    f.write(woosh)

Audio('woosh.wav')

Sample과 woosh mix

참고: Dynamic range compression

  • Dynamic range는 재생 혹은 들을 수 있는 제일 큰 소리와 작은 소리의 차이이다.
  • 나이가 들면서 작은 소리를 잘 듣지 못하므로 증폭기를 사용하는데, 큰 소리까지 증폭하면 너무 시끄러워져 고통스럽게 된다.
  • 사람에 따라 아래 그래프와 같이 곡선을 조정하게 되는데, dB기준 3:1 정도를 기준으로 압축한다.

  • 듣고 싶은 소리와 잡음은 음량이나 주파수와 같은 절대적인 기준으로 구분을 할 수 없으므로 가장 이상적인 보청기는 소리를 분리해 필요한 소리를 크게, 필요없는 소리를 제거할 수 있어야 한다.

  • Transcribe+ Slow Down Music 이라는 무료 apple app이 있다.
    이 app에는 녹음된 음악을 base, drum, vocal, instrument로 나누는 기능이 있다.
  • Python code for audio filtering with the FFT, code